/* vim: set expandtab shiftwidth=4: -*- mode: c++; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: nil -*-
EasyForms
An easy-to-use program for completing scanned documents.
Copyright (C) 2006-2007 Wesley Crossman
Email: wesley@crossmans.net

You can redistribute and/or modify this software under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA */

#include "FormsDocumentScene.h"
#include <QGraphicsSceneMouseEvent>
#include <QSvgRenderer>
#include <QDomDocument>
#include <QTextDocument>
#include <QMessageBox>
#include <QTimer>
#include <iostream>
#include <cmath>

FormsDocumentScene::Type FormsDocumentScene::selected = FormsDocumentScene::TextBox;

FormsDocumentScene::FormsDocumentScene(QObject *parent, const QPixmap &pixmap)
            : QGraphicsScene(parent) {
    document = new FormsDocumentGraphicsItem();
    QPixmap pix(pixmap);
    document->setPixmap(pix);
    setSceneRect(0, 0, pix.width(), pix.height());
    
    addItem(document);
}

void FormsDocumentScene::read(const QString &xml) {
    QDomDocument doc("FormsML");
    if (!doc.setContent(xml)) {
        QMessageBox::warning(NULL, tr("Error Opening Document"),
                tr("The document could not be opened. (Error 3)"));
    }
    QDomElement docElem = doc.documentElement();
    QDomNode n = docElem.firstChild();
    while (!n.isNull()) {
        QDomElement e = n.toElement();
        if (!e.isNull()) {
            if (e.tagName() == "checkbox") {
                addItem(FormsCheckBox::fromElement(e));
            } else if (e.tagName() == "textbox") {
                addItem(FormsTextItem::fromElement(e));
            }
        }
        n = n.nextSibling();
    }
}

void FormsDocumentScene::write(QTextStream &stream) {
    QDomDocument doc("FormsML");
    QDomElement root = doc.createElement("forms");
    doc.appendChild(root);
    foreach (QGraphicsItem *item, items()) {
        FormsItem *formsItem = dynamic_cast < FormsItem * >(item);
        if (formsItem) {
            root.appendChild(formsItem->toElement(doc));
        }
    }
    doc.save(stream, 4);
}

void FormsDocumentScene::writeImage(QIODevice *device) {
    document->pixmap().save(device, "PNG");
}

FormsDocumentGraphicsItem::FormsDocumentGraphicsItem() {
    setAcceptsHoverEvents(true);
}

void FormsDocumentGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    QGraphicsItem::mousePressEvent(event);
    if (event->button() == Qt::LeftButton && FormsTextItem::getMode() == FormsTextItem::Design) {
        QPointF p = event->pos();
        if (FormsDocumentScene::selected == FormsDocumentScene::TextBox) {
            FormsTextItem *textItem = new FormsTextItem("Test");
            scene()->addItem(textItem);
            textItem->move(p + QPointF(0, -textItem->boundingRect().height()/2.0));
        } else if (FormsDocumentScene::selected == FormsDocumentScene::CheckBox) {
            FormsCheckBox *checkbox = new FormsCheckBox();
            scene()->addItem(checkbox);
            checkbox->move(p);
        }
    }
}



static FormsItem::Mode mode = FormsItem::Edit;

void FormsItem::setMode(FormsItem::Mode m, QGraphicsScene *scene) {
    mode = m;
    scene->clearSelection();
    foreach (QGraphicsItem *item, scene->items()) {
        FormsItem *formsItem = dynamic_cast < FormsItem * >(item);
        if (formsItem) formsItem->setToMode();
    }
}

FormsItem::Mode FormsItem::getMode() {
    return mode;
}

FormsItem::FormsItem() {
}

FormsItem::~FormsItem() {
}


qreal FormsCheckBox::defaultSize = 1.0;

FormsCheckBox::FormsCheckBox() {
    checked = false;
    setZValue(1.0);
    setAcceptsHoverEvents(true);
    const qreal size = 15.0;
    slash = new QGraphicsLineItem(0, 0, size, size, this);
    backslash = new QGraphicsLineItem(0, size, size, 0, this);
    slash->setPen(QPen(Qt::black));
    backslash->setPen(QPen(Qt::black));
    setRect(0, 0, size, size);
    setScale(defaultSize);
    setToMode();
}

void FormsCheckBox::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
    QGraphicsRectItem::hoverEnterEvent(event);
    setVisualStatus(true);
}

void FormsCheckBox::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
    QGraphicsRectItem::hoverLeaveEvent(event);
    setVisualStatus(false);
}

void FormsCheckBox::mousePressEvent(QGraphicsSceneMouseEvent *e) {
    QGraphicsRectItem::mousePressEvent(e);
    if (mode == Delete) {
        deleteLater();
        QTimer::singleShot(0, scene(), SLOT(update()));
    } else if (mode == Edit) {
        setChecked(!checked);
        setVisualStatus(true);
    }
}

void FormsCheckBox::setChecked(bool checked) {
    this->checked = checked;
    setVisualStatus(false); //XXX this assumes the mouse isn't over the checkbox
}

void FormsCheckBox::move(QPointF pos) {
    setPos(pos);
}

void FormsCheckBox::setScale(qreal value) {
    scaleValue = value;
    resetTransform();
    scale(value, value);
    QSizeF size = boundingRect().size();
    translate(-size.width()/2.0, -size.height()/2.0);
}

void FormsCheckBox::setToMode() {
    if (mode == Design) {
        setFlags(QGraphicsItem::ItemIsMovable);
    } else {
        setFlags(0);
    }
    setVisualStatus(false);
}

void FormsCheckBox::setVisualStatus(bool value) {
    setBrush(QBrush((mode == Edit || mode == Print) ? Qt::white : Qt::lightGray));
    QPen pen;
    pen.setWidth(1);
    pen.setJoinStyle(Qt::MiterJoin);
    pen.setCapStyle(Qt::RoundCap);
    if (!value && mode == Delete) pen.setColor(QColor(255, 64, 64));
    else if (value && mode == Delete) pen.setColor(Qt::red);
    else if (value || mode == Design || mode == Print) pen.setColor(Qt::black);
    else pen.setColor(QColor(64, 64, 64));
    setPen(pen);
    slash->setPen(pen);
    backslash->setPen(pen);
    slash->setVisible(checked);
    backslash->setVisible(checked);
}

FormsCheckBox *FormsCheckBox::fromElement(QDomElement &e) {
    FormsCheckBox *check = new FormsCheckBox;
    check->setPos(QPointF(e.attribute("x").toDouble(), e.attribute("y").toDouble()));
    check->setChecked(e.attribute("checked").toInt());
    return check;
}

QDomElement FormsCheckBox::toElement(QDomDocument &doc) {
    QDomElement element = doc.createElement("checkbox");
    element.setAttribute("x", x());
    element.setAttribute("y", y());
    element.setAttribute("checked", checked);
    return element;
}


class FormsMoveItem : public QGraphicsRectItem {
public:
    FormsMoveItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = 0)
        : QGraphicsRectItem(x, y, width, height, parent) {}
};

QFont FormsTextItem::defaultFont;

FormsTextItem::FormsTextItem(const QString &text) : QGraphicsTextItem(text) {
    tlHandle = trHandle = blHandle = brHandle = NULL;
    setZValue(1.0);
    setFont(defaultFont);
    setPlainText(text);
    connect(document(), SIGNAL(contentsChanged()), SLOT(updateForNewSize()));

    //create handles
    const int size = 2;
    border = new QGraphicsRectItem(this);
    border->setPen(QColor(0, 0, 0, 63));
    border->setZValue(.9);
    handles.append(tlHandle = new FormsMoveItem(-size, -size, size*2, size*2, this));
    handles.append(trHandle = new FormsMoveItem(-size, -size, size*2, size*2, this));
    handles.append(blHandle = new FormsMoveItem(-size, -size, size*2, size*2, this));
    handles.append(brHandle = new FormsMoveItem(-size, -size, size*2, size*2, this));
    foreach (QGraphicsRectItem *item, handles) {
        item->setZValue(2.0);
        item->setFlags(QGraphicsItem::ItemIsMovable);
    }
    updateForNewSize();
    setToMode();
}

void FormsTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
    if (mode == Edit) {
        setFocus();
        scene()->clearSelection();
        setSelected(true);
        update();
    } else {
        clearFocus();
    }
    QGraphicsTextItem::hoverEnterEvent(event);
    setVisualStatus(true);
}

void FormsTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
    if (mode != Edit) clearFocus();
    QGraphicsTextItem::hoverLeaveEvent(event);
    setVisualStatus(false);
}

void FormsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
    QGraphicsTextItem::mousePressEvent(e);
    if (mode == Delete) {
        deleteLater();
    }
}

void FormsTextItem::setVisualStatus(bool value) {
    foreach (QGraphicsRectItem *item, handles) {
        item->setBrush(QBrush(QColor(0, 0, 0, value ? 255 : 32)));
        item->setPen(QColor(0, 0, 0, value ? 255 : 64));
    }
    border->setPen((mode == Delete) ? Qt::red : QColor(0, 0, 0, value ? 96 : 22));
}

void FormsTextItem::updateForNewSize() {
    tlHandle->setPos(2, 2);
    trHandle->setPos(boundingRect().width()-2, 2);
    blHandle->setPos(2, boundingRect().height()-2);
    brHandle->setPos(boundingRect().width()-2, boundingRect().height()-2);
    border->setRect(0, 0, boundingRect().width(), boundingRect().height());
}

void FormsTextItem::move(QPointF pos) {
    setPos(pos);
}

void FormsTextItem::setToMode() {
    if (mode == Edit) {
        setFlags(QGraphicsItem::ItemIsSelectable);
        setTextInteractionFlags(
                Qt::TextEditable
                |Qt::TextSelectableByMouse
                |Qt::TextSelectableByKeyboard);
    } else {
        setTextInteractionFlags(0);
        if (mode == Design) {
            setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);
        } else {
            setFlags(0);
        }
        clearFocus();
    }
    setVisualStatus(false);
    foreach (FormsMoveItem *item, handles) {
        item->setVisible(mode == Design);
    }
    border->setVisible(mode != Print);
}

FormsTextItem *FormsTextItem::fromElement(QDomElement &e) {
    FormsTextItem *text = new FormsTextItem(e.attribute("text"));
    text->move(QPointF(e.attribute("x").toDouble(), e.attribute("y").toDouble()));
    return text;
}

QDomElement FormsTextItem::toElement(QDomDocument &doc) {
    QDomElement element = doc.createElement("textbox");
    element.setAttribute("x", x());
    element.setAttribute("y", y());
    element.setAttribute("text", toPlainText());
    return element;
}

